import React from 'react';
import PropTypes from 'prop-types';
import cn from 'classnames';
import { Icon } from 'antd';
import './Topology.less';

class Topology extends React.Component {
    constructor(props) {
        super(props);

        this.items = [];
        this.ranges = [];
    }

    componentDidMount() {
        this.align();
        this.addRange();
    }

    componentDidUpdate() {
        const { onDidUpdate } = this.props;
        this.align();
        this.addRange();

        // eslint-disable-next-line no-unused-expressions
        onDidUpdate && onDidUpdate();
    }

    addRange() {
        const { align } = this.props;

        if (align !== 'vertical') return;

        setTimeout(() => {
            // eslint-disable-next-line array-callback-return
            for (let index = 0; index < this.ranges.length; index += 1) {
                const elem = this.ranges[index]
                const belongs = document.querySelectorAll(`.range${index}`);

                if (belongs.length === 0) return;
                const firstSpan = belongs[0].lastElementChild.querySelectorAll('span');
                const lastSpan = belongs[belongs.length - 1].lastElementChild.querySelectorAll('span');
                // 宽度=最后一个元素的左边距 - 第一个元素的左边距
                const width =
                    lastSpan[lastSpan.length - 1].getBoundingClientRect().left -
                    firstSpan[0].getBoundingClientRect().left;

                elem.style.width = `${width}px`;
                elem.style.left = `${firstSpan[0].getBoundingClientRect().right -
                    this.wrap.getBoundingClientRect().left -
                    firstSpan[0].clientWidth / 2}px`;
            }
        }, 100);
    }

    align() {
        const { align } = this.props;

        if (align !== 'horizontal') return;
        for (let i = 0; i < this.items.length; i += 1) {
            const item = this.items[i]
            if (!item) return;
            const next = item.nextElementSibling;

            if (!next) return;
            const first = next.firstElementChild;
            const last = next.lastElementChild;

            if (first && last) {
                item.style.top = `${(first.offsetHeight - last.offsetHeight) / 2}px`;
            }
        }
    }

    renderDom(data, level = 1, hasParent = false) {
        const {
            cursor,
            renderLabel,
            setType,
            setClassName,
            showCloseBtn,
            onClose,
            closeTitle,
            expandedTitle,
            collapsedTitle,
            onClick,
            showCollapseBtn,
            showAddBtn,
            align,
            setRange,
            setCollapse,
            expandIds,
            onExpand,
            fieldsName,
            renderTitle
            // defaultCollapsed
        } = this.props;
        const closeBtn = item => (
            <a
                title={closeTitle}
                className="closeBtn"
                href="#"
                onClick={e => {
                    e.preventDefault();

                    // eslint-disable-next-line no-unused-expressions
                    onClose && onClose(item, data);
                }}
            >
                <i className="fa fa-times-circle" />
            </a>
        );

        return data.map((item, index) => {
            const label = renderLabel ? renderLabel(item) : item[fieldsName.label];
            const collapseView = setCollapse(item, level);
            const expanded = expandIds.includes(`${item.id}_${item.cdType}`);
            let rangeClassName = '';

            // eslint-disable-next-line no-unused-expressions
            setRange &&
                // eslint-disable-next-line array-callback-return
                setRange.map((rangeItem, rangeIndex) => {
                    if (rangeItem.range(item)) {
                        rangeClassName = `range${rangeIndex}`;
                    }
                });

            return (
                <div
                    // style={{ width: `${100 / data.length}%` }}
                    className={cn('item', {
                        [`level-${level}`]: true,
                        [rangeClassName]: true,
                        collapseView: collapseView && !expandIds.includes(`${item.id}_${item.cdType}`)
                    })}
                    // eslint-disable-next-line react/no-array-index-key
                    key={index}
                >
                    <div
                        onClick={e => onClick(item, data, e)}
                        // eslint-disable-next-line no-shadow
                        ref={label => this.items.push(label)}
                        className={cn('topology-label', {
                            'label-border-right':
                                item[fieldsName.children] &&
                                (align === 'vertical' ? true : item[fieldsName.children].length > 1),
                            'label-border-left': hasParent,
                            'label-cursor': cursor === 'pointer',
                            [`label-type-${setType(item)}`]: setType(item),
                            [setClassName(item)]: setClassName(item),
                            hasChild: item[fieldsName.children].length > 0
                        })}
                    >
                        <span title={renderTitle(item)}>{label}</span>
                        {typeof showCloseBtn === 'function'
                            ? showCloseBtn(item, data) && closeBtn(item)
                            : showCloseBtn && closeBtn(item)}
                        {showCollapseBtn &&
                            (collapseView && item[fieldsName.children] && item[fieldsName.children].length > 0) && (
                        // eslint-disable-next-line react/jsx-indent
                            <Icon
                                onClick={e => onExpand(item, level, e)}
                                type={!expanded ? 'plus-circle' : 'minus-circle'}
                                title={
                                    typeof expandedTitle === 'function'
                                        ? expandedTitle(item, fieldsName)
                                        : expandedTitle
                                }
                                className={cn('toggleBtn', {
                                    onlyOneChild: item[fieldsName.children].length === 1
                                })}
                            />
                        // eslint-disable-next-line indent
                        )}
                        {showAddBtn && (item[fieldsName.children] && item[fieldsName.children].length > 0) ? (
                            // eslint-disable-next-line indent
                            <i title={collapsedTitle} className="toggleBtn toggleBtn-collapse fa fa-minus-circle" />
                        ) : (
                            ''
                        )}
                    </div>
                    {(collapseView ? expanded : true) &&
                        item[fieldsName.children] &&
                        item[fieldsName.children].length > 0 && (
                        <div
                            className={cn('sub', {
                                oneChildSub: item[fieldsName.children].length === 1
                            })}
                        >
                            {this.renderDom(item[fieldsName.children], level + 1, true)}
                        </div>
                    )}
                </div>
            );
        });
    }

    // eslint-disable-next-line react/sort-comp
    setRange() {
        const { setRange, fieldsName } = this.props;

        return (
            <div className="topology-range-box">
                {setRange.map((item, index) => {
                    const { range, label, className, ...props } = item;

                    return (
                        <div
                            className={cn('topology-range', className)}
                            // eslint-disable-next-line no-shadow
                            ref={range => this.ranges.push(range)}
                            // eslint-disable-next-line react/no-array-index-key
                            key={index}
                            {...props}
                        >
                            <span>{item[fieldsName.label]}</span>
                        </div>
                    );
                })}
            </div>
        );
    }

    render() {
        const { data, align, className, setRange } = this.props;

        return (
            <div
                // eslint-disable-next-line no-return-assign
                ref={wrap => (this.wrap = wrap)}
                className={cn('ui-topology', `align-${align}`, className)}
            >
                {this.renderDom(data)}
                {setRange && this.setRange()}
            </div>
        );
    }
}

Topology.propTypes = {
    /**
     * 数据结构，包含label、value、children等字段
     */
    data: PropTypes.array,

    /**
     * 点击项的回调函数
     */
    onClick: PropTypes.func,

    /**
     * 鼠标手型：包含默认、手型
     */
    cursor: PropTypes.oneOf(['default', 'pointer']),

    /**
     * 自定义渲染项，参数为item
     */
    // eslint-disable-next-line react/require-default-props
    renderLabel: PropTypes.func,

    /**
     * 增加样式类型，包含：error、success，其他请自己用样式增加
     */
    setType: PropTypes.func,

    /**
     * 增加自定义类名，参数为item
     */
    setClassName: PropTypes.func,

    /**
     * 是否显示关闭按钮，支持bool或func类型
     */
    showCloseBtn: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),

    /**
     * 点击关闭按钮事件回调，参数item，data
     */
    onClose: PropTypes.func,

    /**
     * 关闭按钮提示文字
     */
    closeTitle: PropTypes.string,

    /**
     * 收起按钮的提示文字
     */
    collapsedTitle: PropTypes.string,

    /**
     * 展开/添加按钮的提示问题，如果是function，参数为item
     */
    expandedTitle: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),

    /**
     * 是否显示收起/展开按钮
     */
    showCollapseBtn: PropTypes.bool,

    /**
     * 是否显示新增按钮
     */
    showAddBtn: PropTypes.bool,

    /**
     * 方向
     */
    align: PropTypes.oneOf(['vertical', 'horizontal']),

    /**
     * 设置底部标识范围，包含字段：label、range（函数类型，参数为item即每个项的数据，返回需要包含的条件）、className等，详细请看示例
     */
    // eslint-disable-next-line react/require-default-props
    setRange: PropTypes.array,
    /**
     * 设置是否显示收起展开按钮
     */
    setCollapse: PropTypes.func,
    /**
     * 收起来的 id 集合
     */
    collapseIds: PropTypes.array,
    /**
     * 点击展开或收起时，参数：item、level、e
     */
    onCollapse: PropTypes.func,
    /**
     * 字段别名
     */
    fieldsName: PropTypes.object,
    /**
     * 默认是否收起，暂不支持
     */
    defaultCollapsed: PropTypes.bool
};
Topology.defaultProps = {
    data: [],
    cursor: 'pointer',
    setType: () => {},
    setClassName: () => {},
    showCloseBtn: false,
    onClose: () => {},
    closeTitle: '关闭',
    onClick: () => {},
    // eslint-disable-next-line arrow-body-style
    expandedTitle: (item, fieldsName) => {
        return item[fieldsName.children] && item[fieldsName.children].length > 0 ? '展开' : '添加节点';
    },
    collapsedTitle: '收起',
    showCollapseBtn: false,
    showAddBtn: false,
    align: 'horizontal',
    setCollapse: () => false,
    collapseIds: [],
    onCollapse: () => {},
    fieldsName: {
        label: 'label',
        children: 'children'
    },
    defaultCollapsed: true
};

export default Topology;
